package com.gjd.service.impl;

import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gjd.domain.ResponseResult;
import com.gjd.domain.dto.CodeDto;
import com.gjd.domain.dto.EmailDto;
import com.gjd.domain.entity.CodeObject;
import com.gjd.domain.entity.LoginUser;
import com.gjd.domain.entity.User;
import com.gjd.domain.vo.UserInfoVo;
import com.gjd.domain.vo.UserLoginVo;
import com.gjd.domain.vo.enums.AppHttpCodeEnum;
import com.gjd.handler.CodeAuthenticationHandle;
import com.gjd.mapper.UserMapper;
import com.gjd.service.EmailService;
import com.gjd.service.UserLoginService;
import com.gjd.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@EnableTransactionManagement
public class UserLoginServiceImpl implements UserLoginService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private CodeAuthenticationHandle codeAuthHandle;
    @Autowired
    private EmailService emailService;

    final String content = "您好，您的验证码为";

    @Override
    public ResponseResult login(User user) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
//        Authentication会调用重写后的UserDetailsServiceImpl
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        //判断是否认证通过
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("用户名或密码错误");
        }

        //获取userid,生成token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = String.valueOf(loginUser.getUser().getId());
        String jwt = JwtUtil.createJWT(userId);

        //把用户信息存入redis中
        redisCache.setCacheObject("loginUser:" + userId, loginUser);

        //封装token和userinfo
        //把userInfoVo转换为userLoginVo
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(loginUser.getUser(), UserInfoVo.class);
        UserLoginVo userLoginVo = new UserLoginVo(jwt, userInfoVo);

        return ResponseResult.okResult(userLoginVo);
    }

    @Override
    public ResponseResult loginByEmail(User userInfo) {
        // 查询邮箱是否存在
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getEmail,userInfo.getEmail())
                .eq(User::getUserType,"1"));
        if (user == null){
            // TODO:未指定状态码
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_ERROR);
        }
        // 生成验证码
        String code = RandomCodeUtils.getRandomCode();
        // 验证码存入redis
        CodeDto codeDTO = new CodeDto(code,user);
        redisCache.setCacheObject("userEmail:"+user.getEmail(),codeDTO,5, TimeUnit.MINUTES);
        // 发送验证码
        emailService.send(new EmailDto(Collections.singletonList(user.getEmail()),
                "邮箱验证码", content + code));
        // TODO:状态码未指定
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult handleCode(CodeObject data) {
        return codeAuthHandle.UserCodeAuth(data);
    }

    @Override
    public ResponseResult logout() {
        //从securityContextHolder中获取token，解析获取当前用户的id
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //loginUser是作为authentication的一个属性
        System.out.println(authentication.getPrincipal());
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getUser().getId();

//        Long userId = SecurityUtils.getUserId ();

        //删除redis中对应的值
        redisCache.deleteObject("loginUser:" + userId);
        return ResponseResult.okResult();
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public ResponseResult loginByPhoneAuthCode(User userInfo) {
        // 查询手机是否存在
        User user = this.findByTelephone(userInfo);
        if (user == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_REGISTER);
        }
        try {
            String code = RandomCodeUtils.getRandomCode();
            SendSmsResponse sendSmsResponse = SmsSendUtil.sendSms(user.getPhoneNumber(), code);
            if(sendSmsResponse.getCode().equals("OK")) {
                // 验证码存入redis，设置过期时间为5分钟
                CodeDto codeDTO = new CodeDto(code,user);
                redisCache.setCacheObject("userTelephone:"+user.getPhoneNumber(),codeDTO,5, TimeUnit.MINUTES);
                // 返回201给前端
                return ResponseResult.okResult();
            }else return ResponseResult.errorResult(AppHttpCodeEnum.CODE_SEND_FAIL);
        } catch (ClientException e) {
            throw new RuntimeException("验证码发送出现错误");
        }
    }

    /**
     * 通过手机查询
     */
    public User findByTelephone(User userInfo) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhoneNumber,userInfo.getPhoneNumber())
                .eq(User::getUserType,"1");
        return userMapper.selectOne(wrapper);
    }

    private boolean userTypeExists(String userType) {
        LambdaQueryWrapper<User> Wrapper = new LambdaQueryWrapper<>();
        Integer count = userMapper.selectCount(Wrapper.eq(User::getUserType, userType));
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }

    private boolean passWordExists(String password, String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUserName, username);
        User user = userMapper.selectOne(wrapper);
        String encode = user.getPassword();
        boolean rs = passwordEncoder.matches(password, encode);
        return rs;
    }

    private boolean userNameExists(String userName) {
        LambdaQueryWrapper<User> Wrapper = new LambdaQueryWrapper<>();
        Integer count = userMapper.selectCount(Wrapper.eq(User::getUserName, userName));
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }
}
